package com.smile.frame.seckill.config;

import cn.hutool.core.thread.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import javax.annotation.PreDestroy;
import java.util.concurrent.*;

/**
 * 异步执行任务
 * <p>
 * <p>
 * 当达到最大线程数后，拒绝策略
 * 终止策略 abort
 * 调用者运行策略 callerRuns
 * 丢弃策略 discard
 * 丢弃最老策略 discardOldest
 *
 * @author LGC
 * @EnableScheduling @Scheduled 定时任务使用spring线程池
 * @EnableAsync @Async 异步任务（aop 实现） 使用spring线程池
 * @date 2024/7/8 11:19
 */
@Slf4j
@EnableScheduling
@EnableAsync(proxyTargetClass = true)
@Configuration
public class ThreadPollConfig implements AsyncConfigurer {

    /**
     * 核心线程数 = cpu 核心数 + 1
     */
    private final int core = Runtime.getRuntime().availableProcessors() + 1;


    /**
     * spring 线程池
     * 其实就是对juc 线程池进行了封装
     * Spring 会负责在应用程序关闭时自动关闭所有注册的线程池
     * awaitTerminationSeconds 属性来控制等待时间。在但大多数情况下，不需要手动关闭
     */
    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(core);
        threadPoolTaskExecutor.setMaxPoolSize(core * 2);
        threadPoolTaskExecutor.setQueueCapacity(1024);
        threadPoolTaskExecutor.setThreadNamePrefix("spring-task-");
        // 设置拒绝策略：当线程池达到最大线程数时，如何处理新任务，使用调用者策略，即使用调用者线程去执行
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        // 执行初始化
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }


    /**
     * 自定义 @Async 注解使用系统线程池
     *
     * @return
     */
    @Override
    public Executor getAsyncExecutor() {
        return threadPoolTaskExecutor();
    }

    /**
     * 异步执行异常处理
     *
     * @return
     */
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (throwable, method, objects) -> {
            throw new RuntimeException("异步任务 error", throwable);
        };
    }

    /*********************************juc 线程池********************************/

    private ThreadPoolExecutor threadPoolExecutor;

    /**
     * juc 线程池
     */
    @Bean(name = "threadPoolExecutor")
    public ThreadPoolExecutor threadPoolExecutor() {
        threadPoolExecutor = new ThreadPoolExecutor(
                core, core * 2,
                1, TimeUnit.HOURS,
                new LinkedBlockingQueue<>(1024),
                ThreadFactoryBuilder.create().setNamePrefix("juc-task-").build(),
                // 设置拒绝策略：当线程池达到最大线程数时，如何处理新任务，使用调用者策略，即使用调用者线程去执行
                new ThreadPoolExecutor.CallerRunsPolicy());
        return threadPoolExecutor;
    }


    /**
     * 销毁事件
     */
    @PreDestroy
    public void destroy() {
        try {
            log.info("====关闭后台任务juc线程池,spring 线程池会自动关闭====");
            shutdownAndAwaitTermination(threadPoolExecutor);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 停止线程池
     * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
     * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.
     * 如果仍然超時，則強制退出.
     * 另对在shutdown时线程本身被调用中断做了处理.
     */
    private static void shutdownAndAwaitTermination(ExecutorService pool) {
        if (pool != null && !pool.isShutdown()) {
            pool.shutdown();
            try {
                if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
                    pool.shutdownNow();
                    if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
                        log.info("Pool did not terminate");
                    }
                }
            } catch (InterruptedException ie) {
                pool.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
    }


}
